'''
mess shangweiji

connect: sqlserver (get set) , plcS7 (get)

'''
import copy
import os.path
import threading
import time
import traceback
from enum import Enum

if os.path.exists(r"D:\data\simulate_auto_packing_management.txt"):
    isSim = True
else:
    isSim = False
if not isSim:
    from sql_server.HuoniMessClient import HuoniMessClient
    from plc_S7.HuoniPlcScanClient import HuoniPlcScanClientBase, config as plc_config
    from printer.HoneyPrintClient import HoneyPrintClient
else:
    # from sql_server.HuoniMessClient import SimHuoniMessClient as HuoniMessClient
    # from plc_S7.HuoniPlcScanClient import SimHuoniPlcScanClientBase as HuoniPlcScanClientBase, config as plc_config

    from printer.HoneyPrintClient import SimHoneyPrintClient as HoneyPrintClient
    from plc_S7.HuoniPlcScanClient import HuoniPlcScanClientBase, config as plc_config
    from sql_server.HuoniMessClient import HuoniMessClient

from concurrent.futures import ThreadPoolExecutor
import queue
from log import logger
from util.util import read_yaml, write_yaml

# from main_with_ui import MyQMainWindow
print = logger.info


class BoundThreadPoolExecutor(ThreadPoolExecutor):
    # 对ThreadPoolExecutor 进行重写，给队列设置边界
    def __init__(self, qsize: int = None, *args, **kwargs):
        super(BoundThreadPoolExecutor, self).__init__(*args, **kwargs)
        self._work_queue = queue.Queue(qsize)


# pn_plc_dict = { # pn: plc_address
#     'CBL-500-120-S00-05': plc_config.PlaceCable, # shu ju xian p3
#     # 'CT47-X1N-37D1E0G': plc_config.PlacePhone, # p7
#     # 'CT47-X1N-38D1E0G': plc_config.PlacePhone, # p7
#     'CT47-ML-RS-03': plc_config.PlaceInstruction, # p5
#     'CT45XP-ML-RS-06': plc_config.PlaceInstruction,
#
#     'test1': plc_config.PlaceChDQ, # p4
#     # 'test2': plc_config.PlaceBat, # p2
#     '50183411-001': plc_config.PlaceStrap, # p6
#     # 'EULA-EN-IN-O1': plc_config.PlaceLicence, # p5
#     'EULA-EN-IN-01 Rev A': plc_config.PlaceLicence, # p5
#     '318-055-067': plc_config.PlaceBat, # p2
#
# }

# addition_pns = [ # addition pn needed scan
#     # '3011-9188-001', # HANDSTRAP_CT47
#     # '318-055-021', # CT45/47 BATTERY PACK, STD
#     # 'CT45-ML-RS-07', # REGULATORY SHEET, CT45
# ]

def change_plcAds(pn_plc_dict):
    name_plcAds_dict = {}
    for ad in pn_plc_dict.values():
        ad = eval(ad) if type(ad) == str else ad
        name_plcAds_dict[ad['device_name']] = ad
    return name_plcAds_dict

def pnplcAdsStr2pnplcAds(pnplcAdsStr):
    pnplcAds={}
    for pn, plcAdsStr in pnplcAdsStr.items():
        plcAdsStr = eval(plcAdsStr) if type(plcAdsStr)==str else plcAdsStr
        pnplcAds[pn] = plcAdsStr
    return pnplcAds


def pnplcAdsStr2pnplcAds(pnplcAdsStr):
    pnplcAds = {}
    for pn, plcAdsStr in pnplcAdsStr.items():
        plcAdsStr = eval(plcAdsStr) if type(plcAdsStr) == str else plcAdsStr
        pnplcAds[pn] = plcAdsStr
    return pnplcAds


# name_plcAds_dict = change_plcAds(pn_plc_dict) # name: addresses

#
# main_config = { # pn: plc_address
#     plc_config.PlaceCable:['CBL-500-120-S00-05',], # shu ju xian p3
#     # 'CT47-X1N-37D1E0G': plc_config.PlacePhone, # p7
#     # 'CT47-X1N-38D1E0G': plc_config.PlacePhone, # p7
#     plc_config.PlaceInstruction:['CT47-ML-RS-03'], # p5
#
#     plc_config.PlaceChDQ: ['test1'], # p4
#     plc_config.PlaceBat: ['test2'], # p2
#     plc_config.PlaceStrap:['50183411-001'], # p6
#     plc_config.PlaceLicence:['test4'], # p5
#     plc_config.PlaceBat:['test5']
# }


lock_plc = threading.Lock()


class ShowStatus(Enum):
    WAIT_SCAN = '等待扫码结果'
    WAIT_PRINT = '等待打印'
    WAIT_UPLOAD = '等待上传'
    CHECKING = '比对中'
    PRINTING = '打印中'
    UPLOADING = '上传中'

    PASS = 'PASS'
    NG = 'NG'

    Stop = '未启动'
    Start = '已启动'
    NoUse = '未使用'

    Sim = '仿真'
    SimConnected = '\N{LINK SYMBOL}仿真已连接'
    SimDisConnect = '\N{NO ENTRY}仿真未连接'

    Connected = '\N{LINK SYMBOL}已连接'
    DisConnect = '\N{NO ENTRY}未连接'
    Testing = '检测中'
class Engine:

    def __init__(self, isUI=False, ui_handle=None):
        # # plc handle
        # lock_plc.acquire()
        # self.plc_main_handle = HuoniPlcScanClientBase(plc_config.PlacePhone)
        # lock_plc.release()
        # self.plc_handle.connect()
        # mess handle
        self.mess_handle = HuoniMessClient(driver='sim' if isSim else 'sqlalchemy')
        # printer handle

        self.printer_handle = HoneyPrintClient()


        lock_plc.acquire()
        self.plc_main_handle = HuoniPlcScanClientBase(plc_config.MainTh, driver='sim' if isSim else 's7_tcp')
        lock_plc.release()

        self.plc_handles = []  # saved plc handle for child thread
        self.pool = BoundThreadPoolExecutor(qsize=18, max_workers=18)
        self.sku = 'CT47-X1N-38D1E0G'
        # self.sku = 'CT45P-X0N-37D103G'
        # self.sku = 'CT45P-X0N*'
        self.debug = 0

        self.STATUS = 'Stop'  # Stop, Run, Init  # global
        # self
        self.isUI = isUI
        self.ui_handle = ui_handle

        # self.pn_plc_dict = pn_plc_dict
        # self.pnStr_plcNameStr_dict = {'CT47-ML-RS-03': '点位5说明书', # p5
        #                         'CT45XP-ML-RS-06': '点位5说明书',} # 从文件读
        # self.pnStr_plcNameStr_dict = read_yaml('config/pn2plc_config.yaml')
        self.pn_plc_dict = read_yaml('config/pn2plc_config.yaml')
        self.running_pns = []
        self.res_pd = {}
        #
        self.isSimScanOk = False

        self.lock_show_dict = threading.Lock()
        self.show_dict = {  # 类似和界面共享内存方式,
            'running_thread_info': {  # 运行线程信息
                '点位7手机': {
                    'mess_pn': ['phone_mess_pn', 1],  # [value, bool是否更改了]， 不能是字典
                    'plc_sn': ['phone_plc_sn', 1],
                    'status': [ShowStatus.Stop, 1],  # 等待扫码结果, 比对中， PASS， NG
                },
                '点位2电池': {  # 电池
                    'mess_pn': ['bat_mess_pn', 1],
                    'plc_pn': ['bat_plc_pn', 1],
                    'status': [ShowStatus.Stop, 1],  # 等待扫码结果, 比对中， PASS， NG
                },
                '点位3数据线': {
                    'mess_pn': ['cable_mess_pn', 1],
                    'plc_pn': ['cable_plc_pn', 1],
                    'status': [ShowStatus.Stop, 1],  # 等待扫码结果, 比对中， PASS， NG
                },
                '点位5说明书': {
                    'mess_pn': ['instr_mess_pn', 1],
                    'plc_pn': ['instr_plc_pn', 1],
                    'status': [ShowStatus.Stop, 1],  # 等待扫码结果, 比对中， PASS， NG
                },
                '点位5许可证': {
                    'mess_pn': ['license_mess_pn', 1],
                    'plc_pn': ['license_plc_pn', 1],
                    'status': [ShowStatus.Stop, 1],  # 等待扫码结果, 比对中， PASS， NG
                },
                '点位6腕带': {
                    'mess_pn': ['strap_mess_pn', 1],
                    'plc_pn': ['strap_mess_pn', 1],
                    'status': [ShowStatus.Stop, 1],  # 等待扫码结果, 比对中， PASS， NG
                },
                'label_print': {  # 标贴打印
                    'sn': ['label_print_sn', 1],  # 打印的sn
                    'pof_path': ['label_print_pof', 1],  #
                    'status': [ShowStatus.Stop, 1],  # 等待打印, 打印中， 打印完成
                },
                'record_upload': {  # 上传
                    'pn': ['record_upload_pn', 1],  # 上传的pn
                    'sn': ['record_upload_sn', 1],  # 上传的sn
                    'status': [ShowStatus.Stop, 1],  # 等待上传, 上传中， 上传完成
                },

            },

            'product_info': [[],1],  # 产品信息 todo 加标志位 是否更改

            'uploaded_record': [[],1],  # 已上传记录

            # 'sys_status': '',
            'printer_status': [ShowStatus.Sim, 1]if isSim else [ShowStatus.Testing, 1],  # '已连接' '连接异常' '仿真'
            'plc_status': [ShowStatus.Sim, 1] if isSim else [ShowStatus.Testing, 1],
            'mess_status': [ShowStatus.Sim, 1] if isSim else [ShowStatus.Testing, 1],
            # 'sys_time': '',
        }

    def start(self):
        # init
        # isPrinterOpen = self.printer_handle.open()
        # isPlcConnect = self.plc_main_handle.connect()
        # isMessConnect = self.mess_handle.is_connect()
        # self.SET_SHOW_DICT(self.show_dict,'printer_status',ShowStatus.Connected if isPrinterOpen else ShowStatus.DisConnect)
        # self.SET_SHOW_DICT(self.show_dict, 'plc_status',
        #                    ShowStatus.Connected if isPlcConnect else ShowStatus.DisConnect)
        # self.SET_SHOW_DICT(self.show_dict, 'mess_status',
        #                    ShowStatus.Connected if isMessConnect else ShowStatus.DisConnect)
        # if not (isPlcConnect and isPrinterOpen and isMessConnect):
        #     logger.error(f'设备未就绪 PLC {isPlcConnect}; 打印机 {isPrinterOpen};  Mes {isMessConnect}')
        #     return

        # 0 连接，测试各设备连接情况
        self.pool.submit(device_connect_work_thread, self)

        # 1 start product gotten thread
        self.set_is_stop()
        self.pool.submit(productGotten_work_thread, self)  # status is from init to run

        # 2 start phone thread
        lock_plc.acquire()
        phone_plc_hand = HuoniPlcScanClientBase(plc_config.PlacePhone, driver='sim' if isSim else 's7_tcp')
        phone_plc_hand.connect()
        lock_plc.release()
        self.plc_handles.append(phone_plc_hand)
        self.pool.submit(phone_work_thread, self, phone_plc_hand)

        # 3 start print thread
        # lock_plc.acquire()
        # plc_print_handle = HuoniPlcScanClientBase(plc_config.PlacePhone)
        # lock_plc.release()
        # self.plc_handles.append(plc_print_handle)
        self.pool.submit(print_work_thread, self)

        # 4 start all compare thread
        name_plcAds_dict = change_plcAds(self.pn_plc_dict)  # device_name: plc_address
        for device_name, plc_address in name_plcAds_dict.items():
            if device_name == '点位4充电器':
                continue
            lock_plc.acquire()
            plc_hand = HuoniPlcScanClientBase(plc_address, driver='sim' if isSim else 's7_tcp')
            plc_hand.connect()
            lock_plc.release()
            self.plc_handles.append(plc_hand)
            self.pool.submit(compare_scan_work_thread, self, plc_hand)
        # set_is_init()

        # 5 start upload thread
        self.pool.submit(upload_work_thread, self)
        print(f'self.plc_main_handle.is_connect() {self.plc_main_handle.is_connect()}')
        if self.plc_main_handle.is_connect():
            try:
                self.plc_main_handle.set_is_ready()  # set 1
            except:
                logger.error('PLC通讯异常')
                logger.info(traceback.format_exc())
        # logger.info('Started')

    def close(self):
        if not self.STATUS == 'Run':
            return
        self.plc_main_handle.set_not_ready()

        self.printer_handle.close()
        for plc_handle in self.plc_handles:
            plc_handle.close()
        self.plc_main_handle.close()

    def wait_is_run(self, isBlock=True):
        if isBlock:
            # print('waiting until status is run...')
            while not self.STATUS == 'Run':  # be running
                # print(STATUS)
                time.sleep(1)
            return True
        else:
            return self.STATUS == 'Run'

    def is_run(self):
        return self.STATUS == 'Run'

    def is_run(self):
        return self.STATUS == 'Run'

    def set_is_run(self):
        # before is Init
        self.STATUS = 'Run'
        if self.isUI:
            self.ui_handle.statusChangeSignal.emit([])

    def wait_is_init(self):
        print('waiting until status is init...')
        while not self.STATUS == 'Init':  # be initing
            # print(STATUS)
            time.sleep(1)
        return True

    def set_is_init(self):
        # before is Stop
        if self.STATUS == 'Stop':
            self.STATUS = 'Init'
        else:
            raise 'Software status is error'
        if self.isUI:
            self.ui_handle.statusChangeSignal.emit([])

    def set_is_stop(self):
        # before is Stop
        if self.STATUS == 'Run' or self.STATUS == 'Stop' or self.STATUS == 'Init':
            self.STATUS = 'Stop'
        else:
            raise 'Software status is error'
        if self.isUI:
            self.ui_handle.statusChangeSignal.emit([])

    def SET_SHOW_DICT(self, sub_show_dict=None, key=' ', value='', mode='equal'):
        '''
        更新后端SHOW_DICT，触发前端ui
        mode: 'equal', 'append'
        :return:
        '''
        # source = self.show_dict
        # for k in key:
        #     source = source[k]
        # source = value

        self.lock_show_dict.acquire()
        if mode == 'equal':
            sub_show_dict[key][0] = value
        elif mode == 'append':
            sub_show_dict[key][0].append(value)
        else:
            self.lock_show_dict.release()
            raise Exception(f'mode {mode} 不存在')
        sub_show_dict[key][1] = 1 # 标志位 已更改
        # print(f'sub_show_dict  {sub_show_dict}')
        if self.ui_handle is not None:
            self.ui_handle.statusChangeSignal.emit([])
        # time.sleep(0.5)
        self.lock_show_dict.release()

    def manual_print(self, manual_sn = ''):
        '''
        手动输入sn，打印标贴。和mess，打印机交互；和plc无交互
        :param manual_sn:
        :return:
        '''
        mess_handle = self.mess_handle
        printer_handle = self.printer_handle
        res_pd = self.res_pd # 产品信息
        #part_number = self.sku  # phone pn is the sku
        part_number = self.res_pd['F_Unit_type']  # phone pn is the sku
        serial_number = manual_sn
        # self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info']['label_print'], key='sn',
        #                    value=serial_number)  # 设置show_dict，emit

        # 是否重复打印
        rows = mess_handle.get_packing_record(serial_number)
        if len(rows)>0:
            # 弹出提示框
            return False

        # 查找
        res_labelNames = mess_handle.getLabelName(F_Unit_type=res_pd['F_Unit_type'],
                                                          F_LabelGroupName='KitLabel')  # [{},{}] ones
        print(res_labelNames)
        # F_LabelName = res_labelNames[0]['F_LabelName'] #
        for res in res_labelNames:  # maybe muti labels, now ones
            # get label content
            F_LabelName = res['F_LabelName']
            res_labelContents = mess_handle.getLabelContent(F_LabelName=F_LabelName,
                                                            F_Unit_type=res_pd['F_Unit_type'])  # [{},{}]
            print(f'res_labelContents {res_labelContents}')
            # get print value from qcdcs
            # res_labelvars = mess_handle.getLabelVarFromQCDCS(pn=part_number, sn=serial_number)
            # print(f' res_labelvars {res_labelvars}')  # bda, imei, imei2, meid, meid2, wifi
            # if len(res_labelvars) == 0:
            #     logger.error('printing value from qcdcs is empty!')

            for res_labelContent in res_labelContents:
                if res_labelContent['F_MDCSVariable']:  # is get value from cdcs
                    # value = 'getFromCdcs' # # get from cdcs todo
                    b = res_labelContent['F_MDCSVariableName']

                    # new
                    value = mess_handle.getLabelVarFromQCDCSAndName(sn=serial_number, var_name=b)
                    # if len(value) == 0:
                    #     is_print_ok = False
                    res_labelContent['F_VariableValue'] = value #

                    # old
                    # for res_labelvar in res_labelvars:  # loop twice
                    #     a = res_labelvar['Type']
                    #     if a == b:
                    #         print(f"{res_labelvar['Type']}  {res_labelvar['Value']}")
                    #         # value = res_labelvar['Value']
                    #         res_labelContent['F_VariableValue'] = res_labelvar['Value']
                    #         break
            res_labelContents.append({'F_VariableName': 'ITEM', 'F_VariableValue': part_number})
            res_labelContents.append({'F_VariableName': 'DSN', 'F_VariableValue': serial_number})

            # control printer to print label
            # pof_path = f'./printer/pof/{F_LabelName}'  # \\ch3uw1050\THLabel\Barone\POF\
            pof_path = fr'\\ch3uw1050\THLabel\Barone\POF\{F_LabelName}'
            # self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info']['label_print'],
            #                    key='pof_path',
            #                    value=F_LabelName)  # 设置show_dict，emit
            printer_handle.printOneLabel(res_labelContents, template_path=pof_path)
            logger.error(f'INFO: 手动打印：pn {part_number}; pof {pof_path}; sn {serial_number}')
            # self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info']['label_print'], key='status',
            #                    value=ShowStatus.PASS)  # 设置状态，emit
        return True

    def manual_upload(self, manual_sn = ''):
        '''
        手动输入sn，上传。和mess；和plc无交互
        :param manual_sn:
        :return:
        '''
        mess_handle = self.mess_handle
        F_Unit_type = self.res_pd['F_Unit_type']
        isok, msg = mess_handle.upload_packing_record(manual_sn, F_Unit_type, eid='AUTO_M')
        if isok:  # 成功上传
            pass  # 界面累加一条记录
            time.sleep(0.5)
            rows = mess_handle.get_packing_record(manual_sn)

            print(f'rows {rows}')
            self.SET_SHOW_DICT(sub_show_dict=self.show_dict, key='uploaded_record',
                               value=rows[0], mode='append')
        else:  # 未上传成功
            # record = {'sn': f'SN {manual_sn} 已存在，不再上传'}
            record = {'sn': msg}
            self.SET_SHOW_DICT(sub_show_dict=self.show_dict, key='uploaded_record',
                               value=record, mode='append')

def device_connect_work_thread(self):
    '''
    连接 并 测试各设备连接情况
    :param self:
    :return:
    '''

    isPrinterOpen = self.printer_handle.open()
    if isSim:
        self.SET_SHOW_DICT(self.show_dict, 'printer_status',
                       ShowStatus.SimConnected if isPrinterOpen else ShowStatus.SimDisConnect)
    else:
        self.SET_SHOW_DICT(self.show_dict, 'printer_status',
                       ShowStatus.Connected if isPrinterOpen else ShowStatus.DisConnect)

    isPlcConnect = self.plc_main_handle.connect()
    if isSim:
        self.SET_SHOW_DICT(self.show_dict, 'plc_status',
                       ShowStatus.SimConnected if isPlcConnect else ShowStatus.SimDisConnect)
    else:
        self.SET_SHOW_DICT(self.show_dict, 'plc_status',
                       ShowStatus.Connected if isPlcConnect else ShowStatus.DisConnect)

    isMessConnect = self.mess_handle.is_connect()  # 耗时
    if isSim:
        self.SET_SHOW_DICT(self.show_dict, 'mess_status',
                       ShowStatus.SimConnected if isMessConnect else ShowStatus.SimDisConnect)
    else:
        self.SET_SHOW_DICT(self.show_dict, 'mess_status',
                       ShowStatus.Connected if isMessConnect else ShowStatus.DisConnect)
    if not (isPlcConnect and isPrinterOpen and isMessConnect):
        logger.error(f'设备未就绪 PLC {isPlcConnect}; 打印机 {isPrinterOpen};  Mes {isMessConnect}')


def productGotten_work_thread(self):
    '''

    :param self:
    :return: self.sku, self.res_pd
    '''
    try:
        logger.info('productGotten_work_thread started')
        while self.wait_is_init():

            # # 1 connection
            # print('\n\n1 connection ')
            # hm = HuoniMessClient()
            # # hm.connect()

            # # 2 getting user
            # print('\n\n2 getting user')
            # username = 'e587996'
            # res_user = self.mess_handle.getKitUserByName(username) # Kitting_User
            # print('user...')
            # print(res_user)

            try:
                # sku_front = self.sku.split('-')[0] # CT47-X1N-37D1E0G
                # print(f'sku_front {sku_front}')
                # self.plc_main_handle.set_sku(sku_front)


                # sku_id_dict = {  # sku: id
                #     'CT47-X1N-38D1E0G': 2,
                #     'CT47-X0N-38D100G': 2,
                #     'CT45-L1N-28D120G': 3,
                #     'CT45P-X0N-38D100G': 3,
                #
                #
                # }


                # 1 getting product
                print('\n\n1 getting product')
                # sku = 'CT47-X1N-37D1E0G'
                # sku = 'CT47-X1N-38D1E0G'
                # rows = self.mess_handle.getBzBySku(sku=self.sku)  # 拉 Kitting_newunittype
                rows = self.mess_handle.getBzByUnitType(unit_type=self.sku) # 根据unittype查询 20250212
                if len(rows) > 0:
                    self.sku = rows[0]['F_SKU']
                print(f'product information...')
                if len(rows) == 0:
                    self.SET_SHOW_DICT(sub_show_dict=self.show_dict, key='product_info',
                                       value=[])  # 设置状态，emit
                    logger.error(f'未查询到产品信息 sku {self.sku}')
                    self.set_is_stop()
                    continue

                for k, v in rows[0].items():
                    print(f'{k}: {v}')
                self.res_pd = rows[0]
                res_pd = self.res_pd
                #
                if res_pd['F_NeedCheckQCDCS'] == 1:
                    # 需要查询MDCS中的表tblitems?
                    pass

                if res_pd['F_NeedKitLabel'] == 1:  # 是否需要打印Kit标签
                    pass

                if res_pd['F_BlockStatus'] == 1:
                    # 表示这个SKU是否允许包装
                    # =1表示 不可以包装，需要提醒 这个产品因为质量或者其他问题，暂时不可以包装出货
                    # =0表示可以包装
                    pass

                # 2 FAI and compare
                print('\n\n2 get FAI and compare')
                if not self.debug:
                    res_fai = self.mess_handle.getFAIByUnitType(
                        unit_type=res_pd['F_Unit_type'])  # Kitting_FAIContains \
                    self.SET_SHOW_DICT(sub_show_dict=self.show_dict, key='product_info',
                                       value=copy.deepcopy(res_fai))  # 设置状态，emit
                    self.running_pns = []
                    self.running_place_pns = [] #
                    for res in res_fai:
                        print(res)
                        # for k,v in res.items():
                        #     print(f'{k}: {v}')
                        bom_item_pn = res['f_items']
                        isAddition = bom_item_pn in list(self.pn_plc_dict.keys())
                        is_in_pn_plc_dict = self.pn_plc_dict.get(bom_item_pn) # pn在配置文件里
                        # if (res['FAIStatus'] == 1 or isAddition) and is_in_pn_plc_dict:  # more pn
                        if res['FAIStatus'] == 1 and is_in_pn_plc_dict:  # more pn 需要扫码
                            # 表示需要做FAI检查，输入并比较确认具体的物料的料号是否正确 todo
                            self.running_pns.append(bom_item_pn)  # record

                            # # if bom_item_pn!='CT47-ML-RS-03':
                            # #     continue
                            # # plc_work_thread(addressTable=main_config[bom_item_pn], mess_res=bom_item_pn)
                            # self.pool.submit(compare_scan_work_thread, addressTable=main_config[bom_item_pn], mess_res=bom_item_pn)  #
                            # self.pool.submit(compare_scan_work_thread, self)
                            pass
                        if is_in_pn_plc_dict: # 需要抓取
                            self.running_place_pns.append(bom_item_pn)

                        if res['KeyPartNeedScanSN'] == 1:  #
                            # 需要输入具体的SN
                            pass
                    self.plc_main_handle.set_place_infor(self.running_place_pns, self.pn_plc_dict)  # 发送plc 是否放置信息
                    if self.isUI:
                        self.ui_handle.productInforSignal.emit([])

                    print(f'self.running_pns {self.running_pns}')

                else:
                    # debug
                    self.running_pns = list(self.pn_plc_dict.keys())  # all
                    # for k,v in pn_plc_dict.items():
                    #     # self.pool.submit(plc_work_thread, addressTable=main_config[k], mess_res=k)
                    #     self.pool.submit(compare_scan_work_thread, addressTable=pn_plc_dict[k], mess_res=k)


                # 5 which to upload
                print('\n\n5 which to upload')
                res_itemsUpload = self.mess_handle.getItemsUpload(
                    unit_type=res_pd['F_Unit_type'])  # Kitting_FAIContains  [{},{}]
                print(f'res_itemsUpload {res_itemsUpload}')  # decode todo
                if len(res_itemsUpload) == 0:
                    # nothing to upload
                    pass
                else:
                    for res in res_itemsUpload:
                        # res['F_Items']  this to upload todo
                        pass

                res_labelNames = self.mess_handle.getLabelName(F_Unit_type=res_pd['F_Unit_type'],
                                                               F_LabelGroupName='KitLabel')
                print(f'pof: {res_labelNames}')
                # '''
                # bellow is for each serial number
                # '''
                # self.pool.submit(phone_work_thread, self)
                # self.pool.submit(print_work_thread, self.mess_handle, self.printer_handle, res_pd, part_number=self.sku)

                # phone_work_thread(self.mess_handle, part_number=self.sku)
                # print_work_thread(self.mess_handle, self.printer_handle, res_pd, part_number=self.sku)
                self.set_is_run()  # set run
                time.sleep(0.5)
                # self.plc_main_handle.set_is_ready()  # set 1
                logger.info(self.STATUS)

                # 通知plc 换型成功
                presku_id_dict = {  # sku: id
                    'CT47':2,
                    'CT45':3,
                    'EDA5':4,
                    # ''

                }
                pre_sku = self.sku[:4] if len(self.sku) >= 4 else '0000' # XT47 xilie
                # self.plc_main_handle.set_sku_int(sku_id_dict[self.sku])  # set 1
                if not presku_id_dict.get(pre_sku):
                    presku_id_dict[pre_sku]= 100 # default action value
                self.plc_main_handle.set_sku_int(presku_id_dict[pre_sku])  # set 1
                self.plc_main_handle.set_is_change_product_chufa()  # set 1

            except:
                logger.error(traceback.format_exc())
                # self.set_is_run()
                self.set_is_stop()
    except Exception as e:
        logger.error(f'main: {e}')
        logger.error(traceback.format_exc())


# def phone_work_thread(mess_handle, part_number = ''):
def phone_work_thread(self, plc_phone_handle):
    '''
    bellow is for each serial number
    get sn; is pass;
    :return:
    '''
    try:
        # lock_plc.acquire()
        # plc_phone_handle = HuoniPlcScanClientBase(plc_config.PlacePhone)
        # lock_plc.release()
        mess_handle = self.mess_handle
        device_name = '点位7手机'
        print(device_name)
        plc_phone_handle.set_is_need_check(1)  #
        isOnce = True
        while self.wait_is_run():
            # 1 wait
            # print(f'{device_name} 2 wait scan done')
            if isOnce:
                isOnce = False
                self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info'][device_name], key='status',
                                   value=ShowStatus.Start)  # 设置状态，emit
            # print(self.show_dict)
            plc_phone_handle.wait_plc(plc_phone_handle.get_is_scan_done,
                                      plc_phone_handle.set_zero_is_scan_done,
                                      self.is_run)
            if not self.is_run():  # break when status is not RUN
                self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info'][device_name], key='status',
                                   value=ShowStatus.Stop)
                isOnce = True
                continue

            # 2 读取mess_pn,sn
            # print(f'{device_name} 2 get scan res')
            sn_scan_res = plc_phone_handle.get_scan_res()
            # mess_res = '12345678'  # get code from mess todo
            # print(f'{device_name} sn_scan_res {sn_scan_res}')
            # serial_number = '22306B13A3'
            # part_number = 'CT47-X1N-38D1E0G'
            serial_number = sn_scan_res
            part_number = self.sku  # pn of phone is the sku
            self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info'][device_name], key='plc_sn',
                               value=sn_scan_res)  # 显示 SN，emit
            self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info'][device_name], key='mess_pn',
                               value=part_number)  # 显示mess_pn，emit

            # res_isPass = res_isPass[0]
            # post_status = res_isPassQCDCS[0]['post_status'] # "2"表示可以包装
            # isPass = len(sn_scan_res) > 0 and len(res_isPass) > 0 and res_isPass[0]['post_status'] == 2 and res_isPass[0]['model'] == res_isPass[0][
            #     'BF_Model']
            # isPass = len(sn_scan_res) > 0 # not care mess
            # isPass = serial_number == part_number  #
            # isPass = len(serial_number) == 10 and len(res_isPass) > 0

            # 3 比对
            # print(f'{device_name} 3 checking')
            self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info'][device_name], key='status',
                               value=ShowStatus.CHECKING)  # 设置状态，emit
            res_isPass = mess_handle.getIsPassFromQCDCS(serial=serial_number)  # tblItems  qcdcs??   [{},{}]
            print(F'res_isPass {res_isPass}')
            # is_status_ok = (res_isPass[0]['Pre_Status']==2 and res_isPass[0]['Fa_Status']==2 and res_isPass[0]['Post_Status']==2 )
            try:
                isPass = (len(serial_number) == 10 and len(res_isPass) > 0
                          and res_isPass[0]['model'] == part_number
                          and (res_isPass[0]['Pre_Status']==2 and res_isPass[0]['Fa_Status']==2 and res_isPass[0]['Post_Status']==2 )) # 策略
            except:
                logger.error("手机比对失败：请检查手机是否已测试通过")
                logger.info(traceback.format_exc())
                isPass = False

            # if not isPass and False:
            if self.isSimScanOk:
                isPass = True

            if not isPass:
                logger.error(f'手机比对失败。请检查手机型号是否正确')
                # logger.info(f'phone check: serial_number {serial_number} res_isPass {res_isPass}')
            # 4 发送比对结果
            time.sleep(0.1)
            if not isPass:
                # to plc ng
                plc_phone_handle.set_check_res(2)  # check ng
                # break
            else:
                plc_phone_handle.set_check_res(1)  # check ok
            self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info'][device_name], key='status',
                               value=ShowStatus.PASS if isPass else ShowStatus.NG)  # 设置状态，emit
            # pass
            print(f'{device_name} 手机比对 isPass {1 if isPass else 2}  SN {serial_number} PN {part_number}')

    except Exception as e:
        logger.error(f'{device_name} {e}')
        logger.error(traceback.format_exc())


# def print_work_thread(mess_handle, printer_handle, res_pd, part_number):
def print_work_thread(self):
    '''
    wait, print label, isDone

    :param mess_handle:
    :param printer_handle:
    :param res_pd:
    :param part_number:
    :return:
    '''
    try:
        thread_name = '打印机线程'
        mess_handle = self.mess_handle
        printer_handle = self.printer_handle
        plc_print_handle = self.plc_main_handle

        # lock_plc.acquire()
        # plc_print_handle = HuoniPlcScanClientBase(plc_config.PlacePhone)
        # lock_plc.release()
        print(thread_name)
        while self.wait_is_run():

            res_pd = self.res_pd
            # part_number = self.sku  # phone pn is the sku
            part_number = self.res_pd['F_Unit_type']  # phone pn is the sku
            isOnce = True
            while self.wait_is_run(isBlock=False):
                # 1  wait
                # print(f'\n\n{thread_name} 1 wait for plc to print')
                if isOnce:
                    isOnce = False
                    self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info']['label_print'], key='status',
                                       value=ShowStatus.Start)  # 设置状态，emit
                plc_print_handle.wait_plc(plc_print_handle.get_is_print_chufa,
                                          plc_print_handle.set_zero_is_print_chufa,
                                          self.is_run)  # wait
                if not self.is_run():  # break when status is not RUN
                    self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info']['label_print'], key='status',
                                       value=ShowStatus.Stop)
                    isOnce = True
                    continue

                # 2 get sn, Label Name and label content from mess, then print
                time.sleep(0.1)
                is_print_ok = True
                self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info']['label_print'], key='status',
                                   value=ShowStatus.PRINTING)  # 设置状态，emit
                # print(f'{thread_name} 2 get Label Name and label content')  # need pn, sn
                if self.isSimScanOk:  # 模拟对比时
                    serial_number = '23059B2B78'
                    part_number = 'CT45-L1N-28D120G'
                # elif self.manual_print_sn: # 手动打印
                #     serial_number = self.manual_print_sn
                else:
                    serial_number = plc_print_handle.get_serial_number()
                    # print(f'{thread_name} gotten serial_number {serial_number}')
                    if len(str(serial_number)) == 0:
                        logger.error('打印：从plc端读取的序列号为空!')
                        is_print_ok = False
                self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info']['label_print'], key='sn',
                                   value=serial_number)  # 设置show_dict，emit

                res_labelNames = mess_handle.getLabelName(F_Unit_type=res_pd['F_Unit_type'],
                                                          F_LabelGroupName='KitLabel')  # [{},{}] ones
                # print(res_labelNames)
                # F_LabelName = res_labelNames[0]['F_LabelName'] #
                for res in res_labelNames:  # maybe muti labels, now ones
                    # get label content
                    F_LabelName = res['F_LabelName']
                    res_labelContents = mess_handle.getLabelContent(F_LabelName=F_LabelName,
                                                                    F_Unit_type=res_pd['F_Unit_type'])  # [{},{}]
                    if len(res_labelContents)==0:
                        logger.error('打印：未查询到打印变量')
                        is_print_ok = False
                    # print(f'res_labelContents {res_labelContents}')
                    # get print value from qcdcs
                    # res_labelvars = mess_handle.getLabelVarFromQCDCS(pn=part_number, sn=serial_number)

                    # print(f'{thread_name} res_labelvars {res_labelvars}')  # bda, imei, imei2, meid, meid2, wifi
                    # if len(res_labelvars) == 0:
                    #     logger.error('printing value from qcdcs is empty!')
                    for res_labelContent in res_labelContents:
                        if res_labelContent['F_MDCSVariable']:  # is get value from cdcs
                            # value = 'getFromCdcs' # # get from cdcs todo
                            b = res_labelContent['F_MDCSVariableName']

                            # new
                            value = mess_handle.getLabelVarFromQCDCSAndName(sn=serial_number,var_name=b)
                            # if len(value)==0:
                            #     is_print_ok = False
                            res_labelContent['F_VariableValue'] = value
                            # #  yuamfangshi
                            # for res_labelvar in res_labelvars:  # loop twice
                            #     a = res_labelvar['Type']
                            #     if a == b:
                            #         print(f"{res_labelvar['Type']}  {res_labelvar['Value']}")
                            #         # value = res_labelvar['Value']
                            #         res_labelContent['F_VariableValue'] = res_labelvar['Value']
                            #         break

                    res_labelContents.append({'F_VariableName': 'ITEM', 'F_VariableValue': part_number})
                    res_labelContents.append({'F_VariableName': 'DSN', 'F_VariableValue': serial_number})

                    # control printer to print label
                    # pof_path = f'./printer/pof/{F_LabelName}'  # \\ch3uw1050\THLabel\Barone\POF\
                    pof_path = fr'\\ch3uw1050\THLabel\Barone\POF\{F_LabelName}'
                    self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info']['label_print'],
                                       key='pof_path',
                                       value=F_LabelName)  # 设置show_dict，emit
                    if is_print_ok:
                        is_print_ok = printer_handle.printOneLabel(res_labelContents, template_path=pof_path)
                    # to plc, print done
                    plc_res = 1 if is_print_ok else 2
                    plc_print_handle.set_is_print_done(plc_res)  # 1  print done
                    self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info']['label_print'], key='status',
                                       value=ShowStatus.PASS)  # 设置状态，emit
                    print(f'{thread_name} 打印结果 {plc_res} 打印PN {part_number} 打印SN {serial_number} 打印模板 {pof_path}')
                    # plc_print_handle.set_check_res(1)
                    break # 20240704
                # 8 如果需要做关键物料的SN追溯，需要把数据保存到这个表
                pass
    except Exception as e:
        logger.error(e)
        logger.error(traceback.format_exc())
        printer_handle.close()
    finally:
        if printer_handle is not None:
            printer_handle.close()


# def compare_scan_work_thread(addressTable=plc_config.PlaceBat, mess_res='12345678'):
def compare_scan_work_thread(self, plc_handle=''):
    '''
    for comparing scan result with mess
    isNeedScan; wait; check; done
    :param addressTable:
    :param mess_res:
    :return:
    '''
    try:
        # device_name = addressTable['device_name']
        # lock_plc.acquire()
        # plc_handle = HuoniPlcScanClientBase(addressTable)
        # lock_plc.release()
        device_name = plc_handle.addressTable['device_name']
        print(f'{device_name}')
        self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info'][device_name], key='status',
                           value=ShowStatus.Stop)
        # isInitDone = wait_run()
        isOnce1 = True
        while self.wait_is_run():  #
            # addressTable = pn_plc_dict[[]]
            isNeedScan = False  # 根据mess确定是否需要启动
            isNeedScan = 0
            for pn in self.running_pns:
                if not self.pn_plc_dict.get(pn):
                    continue
                # plc_ads = pn_plc_dict[pn]
                plc_ads = eval(self.pn_plc_dict[pn])
                if plc_ads['device_name'] == plc_handle.addressTable['device_name']:
                    isNeedScan = True
                    isNeedScan = 1
                    mess_res = pn
                    break
            plc_handle.set_is_need_check(isNeedScan)  #
            if isNeedScan:
                print(f'{device_name} isNeedScan {isNeedScan}')
            else: # mess_bom 或 配置中没有该pn  20240612
                if isOnce1:
                    isOnce1 = False
                    self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info'][device_name], key='status',
                                       value=ShowStatus.NoUse)
            time.sleep(1)

            # print(f'xxxxxx {addressTable}')
            isOnce = True
            while isNeedScan and self.wait_is_run(isBlock=False):  # 2rd
                # plc_handle.set_addressTable(addressTable) #
                # hn_plc = HuoniPlcScanClientBase(addressTable=config.PlaceBat)
                # self.connect()

                # 2 wait scan done
                if isOnce:
                    isOnce = False
                    self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info'][device_name], key='status',
                                       value=ShowStatus.Start)
                self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info'][device_name], key='mess_pn',
                                   value=mess_res)
                # print(f'{device_name} 2 wait scan done')
                # isDone = 0  # 1 ture 2 false
                # while isDone == 0:
                #     isDone = plc_handle.get_is_scan_done()
                #     if not self.wait_is_run(isBlock=False): # break when STATUS change
                #         break
                #     time.sleep(0.1)  # s
                # plc_handle.set_zero_is_scan_done()  # to 0
                # print(f'{device_name} isDone {isDone}')
                # if not self.wait_is_run(isBlock=False):# break when STATUS change
                #     break

                plc_handle.wait_plc(plc_handle.get_is_scan_done,
                                    plc_handle.set_zero_is_scan_done,
                                    self.is_run)

                if not self.is_run():  # break when status is not RUN
                    self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info'][device_name], key='status',
                                       value=ShowStatus.Stop)
                    break

                # 3 get scan res and check by mess
                # print(f'{device_name} 3 get scan res and check by mess')
                scan_res = plc_handle.get_scan_res()
                self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info'][device_name], key='status',
                                   value=ShowStatus.CHECKING)
                self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info'][device_name], key='plc_pn',
                                   value=scan_res)
                # mess_res = '12345678'  # get code from mess todo
                # print(f'{device_name} scan_res {scan_res}')
                # print(f'{device_name} mess_res {mess_res}')
                isRight = 2  # 1 ture 2 false
                # if scan_res == mess_res or (mess_res == 'test5' and len(scan_res)>0): # now bat is always ok
                if scan_res == mess_res:
                    isRight = 1
                # isRight = 1 # always true

                # 4 return isRight to plc
                time.sleep(0.2)
                # print(f'{device_name} 4 return isRight to plc')
                if self.isSimScanOk:
                    isRight = 1
                plc_handle.set_check_res(isRight)
                print(f'{device_name} 扫码比对 isRight {isRight} scan_res {scan_res} mess_res {mess_res}')
                self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info'][device_name], key='status',
                                   value=ShowStatus.PASS if isRight == 1 else ShowStatus.NG)
                # hn_plc.set_scan_res('!123qwer12fsfder3')
                # value = hn_plc.get_scan_res()
                # print(value)

                # self.close()
    except Exception as e:
        print(traceback.format_exc())
        logger.error(f'{device_name} {e}')
        logger.error(traceback.format_exc())


def upload_work_thread(self):
    '''
    wait, upload, isDone
    input: sn, self.res_pd, self.mess_handle, self.plc_main_handle
    output:
    :return:
    '''
    try:
        thread_name = 'upload work thread'
        mess_handle = self.mess_handle
        plc_main_handle = self.plc_main_handle
        print(thread_name)
        while self.wait_is_run():

            F_Unit_type = self.res_pd['F_Unit_type']
            # part_number = self.sku # phone pn is the sku
            isOnce = True
            while self.wait_is_run(isBlock=False):
                # 1  wait
                # print(f'\n\n{thread_name} 1 wait for plc to upload')
                if isOnce:
                    isOnce = False
                    self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info']['record_upload'],
                                       key='status',
                                       value=ShowStatus.Start)
                plc_main_handle.wait_plc(plc_main_handle.get_is_upload_chufa,
                                         plc_main_handle.set_zero_is_upload_chufa,
                                         self.is_run)  # wait
                logger.error('调试：已收到上传触发')
                if not self.is_run():  # break when status is not RUN
                    self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info']['record_upload'], key='status',
                                       value=ShowStatus.Stop)
                    isOnce = True
                    continue

                # 2 upload
                # print(f'{thread_name} 2 upload')
                # is_upload_ok = True
                self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info']['record_upload'], key='status',
                                   value=ShowStatus.UPLOADING)
                if self.isSimScanOk:  # 模拟, 不从plc读取
                    serial_number = '23059B2B78'
                    F_Unit_type = 'CT45-L1N-28D120G'  # maybe pn
                else:
                    # serial_number = plc_main_handle.get_serial_number()
                    serial_number = plc_main_handle.get_serial_number_to_upload()
                    # print(f'{thread_name} gotten serial_number {serial_number}')
                    if len(str(serial_number)) == 0:
                        logger.error('上传线程：SN读取为空!')
                logger.error(f'调试：上传序列号 {serial_number}')
                self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info']['record_upload'], key='sn',
                                   value=serial_number)
                self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info']['record_upload'], key='pn',
                                   value=F_Unit_type)
                # mess_handle.upload_packing_record(serial_number, res_pd['F_Unit_type'])
                isok, msg = mess_handle.upload_packing_record(serial_number, F_Unit_type)
                if isok:  # 成功上传
                    pass  # 界面累加一条记录
                    time.sleep(0.5)
                    rows = mess_handle.get_packing_record(serial_number)
                    # if len(rows)==0:
                    #     rows = mess_handle.get_packing_record(serial_number)
                    #     if len(rows)==0:
                    #         is_upload_ok = False
                    print(f'rows {rows}')
                    self.SET_SHOW_DICT(sub_show_dict=self.show_dict, key='uploaded_record',
                                       value=rows[0], mode='append')
                else: # 未上传成功
                    # record = {'sn': f'SN {serial_number} 已存在，不再上传'}
                    record = {'sn': msg}
                    self.SET_SHOW_DICT(sub_show_dict=self.show_dict, key='uploaded_record',
                                       value=record, mode='append')
                # 3 is done
                # print(f'{thread_name} 3 uploaded {isok}')
                plc_res = 1 if isok else 2
                plc_main_handle.set_is_upload_done(plc_res)  # 1  uploaded done
                self.SET_SHOW_DICT(sub_show_dict=self.show_dict['running_thread_info']['record_upload'], key='status',
                                   value=ShowStatus.PASS)
                print(f'{thread_name} 上传结果 {plc_res} 上传序列号 {serial_number} 上传sku {F_Unit_type}')
                pass

    except Exception as e:
        logger.error(e)
        logger.error(traceback.format_exc())





if __name__ == '__main__':
    # test_HuoniMessClient()
    Engine().start()
    # eng = Engine()
    # eng.SET_SHOW_DICT(sub_show_dict=eng.show_dict['running_thread_info']['record_upload'], key='status',
    #                value=ShowStatus.PASS)